package hasthi.tools;

import static hasthi.common.constants.WSDMConstants.GROUP_PARAM;
import static hasthi.common.constants.WSDMConstants.HOSTNAME_PARAM;
import static hasthi.common.constants.WSDMConstants.RESOURCE_CATEGORY_PARAM;
import static hasthi.common.constants.WSDMConstants.RESOURCE_TYPE_PARAM;
import static hasthi.wsdm.capabilities.MetricsCapability.FALIED_REQUESTS;
import static hasthi.wsdm.capabilities.MetricsCapability.LAST_REQUEST_RECIVED_TIME;
import static hasthi.wsdm.capabilities.MetricsCapability.LAST_RESPONSE_TIME;
import static hasthi.wsdm.capabilities.MetricsCapability.MAX_RESPONSE_TIME;
import static hasthi.wsdm.capabilities.MetricsCapability.NUMBER_OF_SUCCESSFUL_REQUESTS;
import static hasthi.wsdm.capabilities.MetricsCapability.PENDING_REQUESTS;
import static hasthi.wsdm.capabilities.MetricsCapability.SERVICE_START_TIME;
import hasthi.common.BaseContext;
import hasthi.common.HasthiException;
import hasthi.common.LogFile;
import hasthi.common.ManagementNotifier;
import hasthi.common.Utils;
import hasthi.common.HasthiException.FaultCode;
import hasthi.common.constants.HasthiConstants;
import hasthi.common.constants.WSDMConstants.SystemState;
import hasthi.container.CommunicationContianier;
import hasthi.container.ExtendedXbeanBasedService;
import hasthi.container.MessageProcessor;
import hasthi.container.SoapContainer;
import hasthi.container.XSULMessageContext;
import hasthi.container.XsulBasedWSDMProcessor;
import hasthi.metamodel.resource.ManagedResource.ResourceType;
import hasthi.test.FooDocument;
import hasthi.test.FooResponseDocument;
import hasthi.wsdm.AbstractManagedSystemHandle;
import hasthi.wsdm.ManagementAgentContext;
import hasthi.wsdm.ManagementAgentUtils;

import java.io.IOException;
import java.net.ServerSocket;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.xml.namespace.QName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xmlbeans.XmlObject;
import org.xmlpull.v1.builder.XmlDocument;

import xsul.invoker.soap_over_http.SoapHttpDynamicInfosetInvoker;
import xsul.message_router.MessageContext;
import xsul.message_router.MessageProcessingException;
import xsul.processor.DynamicInfosetProcessorException;
import xsul.soap.SoapUtil;
import xsul.soap11_util.Soap11Util;
import xsul.soap12_util.Soap12Util;
import xsul.xbeans_util.XBeansUtil;
import xsul.xservo_soap_http.HttpBasedServices;

public class TemplateService {
    private static Log log = LogFactory.getLog(HasthiConstants.LOGGER_NAME);
    private String  serviceUrl;
    private static boolean scheduledKill = true;
    private HttpBasedServices httpServices;
    private float failureProb = 0.01f;
    private String group;
    private boolean istransient;
    
    
    
    public FooResponseDocument foo(FooDocument request) throws InterruptedException{
        long start = System.currentTimeMillis();
        while(System.currentTimeMillis()-start < 10){
            System.getenv();
        }
        FooResponseDocument document = FooResponseDocument.Factory.newInstance();
        document.addNewFooResponse().setOut(request.getFoo().getIn());
        return document;
    }
    
    public XmlObject addService(XmlObject req){
        return req;
    }

    public XmlObject removeService(XmlObject req){
        return req;
    }

    public int start() throws HasthiException{
        return start(0);
    }
    
    public int start(int port) throws HasthiException{
        return start(port, randomServiceName(port), randomServiceName(port), null,0.01f,Utils.getLocalHost());
    }
    
    public int start(int port,QName serviceName,QName serviceType,ResourceType category,float failureProb,String group) throws HasthiException{
        try {
            this.failureProb = failureProb;
            this.group = group;
            if(port == 0){
                ServerSocket tempSocket = new ServerSocket(0);
                port = tempSocket.getLocalPort();
                tempSocket.close();
            }
            if(category == null){
                category = ResourceType.TransientService;
            }
            istransient = category.equals(ResourceType.TransientService);
            
            httpServices = new HttpBasedServices(port);
            String cwsdlLoc = Thread.currentThread().getContextClassLoader().getResource("hasthi/tools/dummyservice.wsdl").toString();
            
            OverServiceWrapper service = new OverServiceWrapper("dummy", cwsdlLoc,this,serviceName);
            httpServices.addService(service).startService();
            
            Map<QName, Object> properties = ManagementAgentUtils.createServiceManagementProperties(service.getWsdl());
            
            properties.put(RESOURCE_CATEGORY_PARAM, category.toString());
            properties.put(RESOURCE_TYPE_PARAM,serviceType.toString());
            properties.put(HOSTNAME_PARAM, Utils.getLocalHost());
            properties.put(GROUP_PARAM, group);
            
            
            BaseContext baseContext = new BaseContext();
            service.initManagmentAgent(baseContext,new RandomXsulSystemHandle(serviceName,properties,service) );
            
            serviceUrl = service.getWsdlLocationToUse().replaceAll("\\?wsdl", "");
            log.debug(serviceUrl+ " started");
            return port;
        } catch (DynamicInfosetProcessorException e) {
            throw new HasthiException(e,FaultCode.InitalizationError);
        } catch (IOException e) {
            throw new HasthiException(e,FaultCode.InitalizationError);
        }
    }
    
    
    public class RandomXsulSystemHandle extends AbstractManagedSystemHandle {
        private int requestSucessful;
        private int requestFalied;
        private long maxRequestTime;
        private long lastRequestProcessingTime;
        private Date startTime = new Date();
        private ExtendedXbeanBasedService service;
        private long lastRequestReceived;
        private long pendingRequests;
        private Random r = new Random();
        protected final Map<QName, Object> systemProperties = new HashMap<QName, Object>();
        //private boolean istransient;
        
        public RandomXsulSystemHandle(final QName name, Map<QName, Object> properties,
                final ExtendedXbeanBasedService service) throws HasthiException {
            super(name, name.getLocalPart());
            this.service = service;
            supportedCapabilityNames.add("http://extreme.indiana.edu/wsdm/TestingHelpCapability");
            supportedMatricNames.add(PENDING_REQUESTS);
            supportedMatricNames.add(FALIED_REQUESTS);
            supportedMatricNames.add(MAX_RESPONSE_TIME);
            supportedMatricNames.add(LAST_RESPONSE_TIME);
            supportedMatricNames.add(NUMBER_OF_SUCCESSFUL_REQUESTS);
            supportedMatricNames.add(SERVICE_START_TIME);
            supportedMatricNames.add(LAST_REQUEST_RECIVED_TIME);
            systemProperties.putAll(properties); 
            //this.istransient = istransientVal;
        }

        @Override
        public void init(ManagementAgentContext agentContext) throws HasthiException {
            super.init(agentContext);
            
            Thread t = new Thread(new Runnable() {
                public void run() {
                    while(true){
                        try {
                            int newReqCount = 10*(int)Math.abs(r.nextGaussian());
                            lastRequestReceived = System.currentTimeMillis() - 10*(int)Math.abs(r.nextGaussian());
                            for(int i =0;i<newReqCount;i++){
                                if(Utils.selectWithProbability(0.1f)){
                                    requestFalied++;
                                }else{
                                    requestSucessful++;
                                }
                                lastRequestProcessingTime = (long)Math.abs(r.nextGaussian()) + 10;
                                if(lastRequestProcessingTime > maxRequestTime){
                                    maxRequestTime = lastRequestProcessingTime;
                                }
                            }
                            pendingRequests = Math.abs(pendingRequests + (r.nextBoolean()?+1:-1)*r.nextInt(5));
                            float probAdjust = 60*60/baseContext.getMngAgentEpochSeconds();
                            if(Utils.selectWithProbability(failureProb/probAdjust)){
                                shutDown();
                                log.info("ServiceDied: "+service.getName());
                                Utils.append2file("shutdown.log", "Service Died "+name);
                                break;
                            }
                            Thread.sleep(baseContext.getMngAgentEpochSeconds()*1000);
                        } catch (Exception e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            });
            t.start();

        }



        public void setConfiguration(QName name, Object value) throws HasthiException {
            systemProperties.put(name, value);
        }

        public Object getConfiguration(QName name) throws HasthiException {
            return systemProperties.get(name);
        }

        public SystemState getCurrentSystemState() {
            if (pendingRequests > 200) {
                return SystemState.SaturatedState;
            } else if (pendingRequests > 0) {
                return SystemState.BusyState;
            } else {
                return SystemState.IdleState;
            }
        }

        public SystemState[] getLastStateTransition() {
            if (pendingRequests > 10) {
                return new SystemState[] { SystemState.BusyState, SystemState.SaturatedState };
            } else if (pendingRequests > 0) {
                return new SystemState[] { SystemState.IdleState, SystemState.BusyState };
            } else {
                if (requestSucessful > 0) {
                    return new SystemState[] { SystemState.BusyState, SystemState.IdleState };
                } else {
                    return new SystemState[] { SystemState.StoppedState, SystemState.IdleState };
                }
            }
        }

        public ManagementNotifier getNotifier() {
            return null;
        }

        
        
        public Object getMatric(QName name) throws HasthiException {
            if (name.equals(PENDING_REQUESTS)) {
                return String.valueOf(pendingRequests);
            } else if (name.equals(FALIED_REQUESTS)) {
                return String.valueOf(requestFalied);
            } else if (name.equals(MAX_RESPONSE_TIME)) {
                return String.valueOf(maxRequestTime);
            } else if (name.equals(LAST_RESPONSE_TIME)) {
                return String.valueOf(lastRequestProcessingTime);
            } else if (name.equals(NUMBER_OF_SUCCESSFUL_REQUESTS)) {
                return String.valueOf(requestSucessful);
            } else if (name.equals(SERVICE_START_TIME)) {
                return String.valueOf(startTime.getTime());
            } else if (name.equals(LAST_REQUEST_RECIVED_TIME)) {
                return String.valueOf(lastRequestReceived);
            }
            return null;
        }

 
        public String getSystemEndPoint() {
            return service.getWsdlLocationToUse().replace("?wsdl", "");
        }

        public void shutDown() throws HasthiException {
            try {
                //log.info("Ignore Shutiing down service "+getServiceUrl());
                service.shutdownService();
                httpServices.shutdown();
                log.info("Shutiing down service "+getServiceUrl());
                httpServices.stop();
            } catch (Throwable e) {
            }
            
        }
    }
    
    
    
    
    public void invoke(){
        SoapHttpDynamicInfosetInvoker invoker = new SoapHttpDynamicInfosetInvoker(this.serviceUrl);
        FooDocument fooRequest = FooDocument.Factory.newInstance();
        fooRequest.addNewFoo().setIn("Hello");
        invoker.invokeMessage(XBeansUtil.xmlObjectToXmlElement(fooRequest));
    }
    
    
    
    public String getServiceUrl() {
        return serviceUrl;
    }

    //private static Random rand = new Random();
    public static QName randomServiceName(int port){
        return new QName("http://extreme.indiana.edu/dummyService","Foo"+port);
    }
    
    public static void main(String[] args) throws Exception{
//        BaseContext context = new BaseContext();
//        MessageProcessor processor = new MessageProcessor() {
//        
//            public XmlObject processMessage(hasthi.container.MessageContext messageContext)
//                    throws HasthiException {
//                // TODO Auto-generated method stub
//                return null;
//            }
//        
//            public void addContainer(CommunicationContianier container) throws HasthiException {
//                // TODO Auto-generated method stub
//        
//            }
//        
//        }; 
//        
//        SoapContainer container = new SoapContainer(context, processor);
//        
//        container.start();
//        
//        ManagementAgentContext agentcontext = new ManagementAgentContext(context);
//        XsulBasedWSDMProcessor wsdmprocessor = new XsulBasedWSDMProcessor(agentcontext);
//        processor.addResource(systemHandle);
//        
//        
//        
//        int port = 0;
//        QName serviceType = null;
//        TemplateService dummyService = new TemplateService();
//        if(args.length == 0){
//            dummyService.start(0);
//        }else{
//            if(args.length > 0){
//                port = Integer.parseInt(args[0]);
//            }
//            if(args.length > 1){
//                serviceType = new QName("http://extreme.indiana.edu/dummyservice/",args[1]);
//            }
//            String group;
//            if(args.length > 2){
//                group = args[2];
//            }else{
//                group = Utils.getLocalHost();
//            }
//            
//            if(serviceType != null){
//                dummyService.start(port, new QName(serviceType.getNamespaceURI(),
//                        serviceType.getLocalPart()+"_"+Utils.getLocalHost() + System.currentTimeMillis()), serviceType, null,0.01f,group);
//            }
//        }
//        System.out.println(dummyService.getServiceUrl());        
    }
    
    
    public class OverServiceWrapper extends ExtendedXbeanBasedService{

        public OverServiceWrapper(String name, String wsdlLoc, Object serviceImpl, QName serviceName)
                throws HasthiException {
            super(name, wsdlLoc, serviceImpl, serviceName);
        }

        public OverServiceWrapper(String name, String wsdlLoc, Object serviceImpl)
                throws HasthiException {
            super(name, wsdlLoc, serviceImpl);
        }

        public OverServiceWrapper(String name) throws HasthiException {
            super(name);
        }

        @Override
        public boolean process(MessageContext messageContext) throws MessageProcessingException {
            XSULMessageContext hasthiMessageContext = new XSULMessageContext(messageContext.getIncomingEnvelopeDoc(),
                    getWsdlLocationToUse().replace("?wsdl", ""));
            String soapAction = hasthiMessageContext.getSoapAction();
            if(soapAction != null && soapAction.equals("urn:removeService") || soapAction.equals("urn:addService")){
                SoapUtil soapUtil = SoapUtil.selectSoapFragrance(messageContext.getIncomingEnvelopeDoc(), 
                        new SoapUtil[]{Soap12Util.getInstance(),Soap11Util.getInstance()});
                XmlObject responseAsXmlbeans = hasthiMessageContext.getRequest();
                XmlDocument responseDocument = soapUtil.wrapBodyContent(XBeansUtil.xmlObjectToXmlElement(responseAsXmlbeans));
                messageContext.setOutgoingMessage(responseDocument.getDocumentElement());
                return true;
            }else{
                return super.process(messageContext);    
            }
        }
        
        
        
    }
}
